#include "libavutil/avutil.h"
#include "libavformat/avformat.h"


//p16 ./metedata /path/xxx.mp4 out.mp4
//flv 会有问题 文件问题？

int main(int argc, char **argv) {

    av_log_set_level(AV_LOG_INFO);
    if (argc < 5) {
        av_log(NULL, AV_LOG_ERROR, "Usage: %s <infileName> <startTime> <endTime> <outfileName>  \n", argv[0]);
        return -1;
    }

    const char *inFileName = argv[1];
    int startTime = atoi(argv[2]);
    int endTime = atoi(argv[3]);
    const char *outFileName = argv[4];

    AVFormatContext *inFmtCtx = NULL;
    int ret = avformat_open_input(&inFmtCtx, inFileName, NULL, NULL);
    if (ret != 0) {
        av_log(NULL, AV_LOG_ERROR, "open file failed\n");
        return -1;
    }

    ret = avformat_find_stream_info(inFmtCtx, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "find stream info failed\n");
        goto fail;
    }
    AVFormatContext *outFmtCtx = NULL;
    ret = avformat_alloc_output_context2(&outFmtCtx, NULL, NULL, outFileName);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "avformat_alloc_output_context2 failed\n");
        goto fail;
    }

    for (int i = 0; i < inFmtCtx->nb_streams; i++) {
        AVStream *inStream = inFmtCtx->streams[i];
        AVStream *outStream = avformat_new_stream(outFmtCtx, NULL);
        if (outStream == NULL) {
            ret = -1;
            av_log(NULL, AV_LOG_ERROR, "avformat_new_stream null\n");
            goto fail;
        }

        ret = avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "avcodec_parameters_copy failed\n");
            goto fail;
        }
        outStream->codecpar->codec_tag = 0;
    }

    if (!(outFmtCtx->oformat->flags & AVFMT_NOFILE)) {
        ret = avio_open(&outFmtCtx->pb, outFileName, AVIO_FLAG_WRITE);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "avio_open failed\n");
            goto fail;
        }
    }

    ret = avformat_write_header(outFmtCtx, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "avformat_write_header failed:%s\n", av_err2str(ret));
        goto fail;
    }

    ret = av_seek_frame(inFmtCtx, -1, startTime * AV_TIME_BASE, AVSEEK_FLAG_ANY);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "av_seek_frame failed: %s\n", av_err2str(ret));
        goto fail;
    }

    int64_t *startPTS = av_malloc_array(inFmtCtx->nb_streams, sizeof(int64_t));
    memset(startPTS, 0, inFmtCtx->nb_streams * sizeof(int64_t));
    int64_t *startDTS = av_malloc_array(inFmtCtx->nb_streams, sizeof(int64_t));
    memset(startDTS, 0, inFmtCtx->nb_streams * sizeof(int64_t));

    AVPacket *packet = av_packet_alloc();
    if (!packet) {
        av_log(NULL, AV_LOG_ERROR, "av_packet_alloc failed\n");
        ret = -1;
        goto fail;
    }
    while (av_read_frame(inFmtCtx, packet) == 0) {
        AVStream *inStream = inFmtCtx->streams[packet->stream_index];
        AVStream *outStream = outFmtCtx->streams[packet->stream_index];
        if (endTime < packet->pts * av_q2d(inStream->time_base)) {
            av_packet_unref(packet);
            break;
        }
        if (startPTS[packet->stream_index] == 0) {
            startPTS[packet->stream_index] = packet->pts;
        }
        if (startDTS[packet->stream_index] == 0) {
            startDTS[packet->stream_index] = packet->pts;
        }
        packet->pts = av_rescale_q(packet->pts - startPTS[packet->stream_index], inStream->time_base, outStream->time_base);
        packet->dts = av_rescale_q(packet->dts - startDTS[packet->stream_index], inStream->time_base, outStream->time_base);
        if (packet->pts < 0) {
            packet->pts = 0;
        }
        if (packet->dts < 0) {
            packet->pts = 0;
        }
        packet->duration = av_rescale_q(packet->duration, inStream->time_base, outStream->time_base);
        packet->pos = -1;

        if (packet->pts < packet->dts) {
            av_packet_unref(packet);
            continue;
        }

        ret = av_interleaved_write_frame(outFmtCtx, packet);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "av_interleaved_write_frame failed: %s\n", av_err2str(ret));
            av_packet_unref(packet);
            break;
        }
        av_packet_unref(packet);
    }

    av_write_trailer(outFmtCtx);


    fail:
    if (inFmtCtx) {
        avformat_close_input(&inFmtCtx);
    }
    if (outFmtCtx && !(outFmtCtx->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&outFmtCtx->pb);
    }
    if (outFmtCtx) {
        avformat_free_context(outFmtCtx);
    }
    if (startPTS) {
        av_freep(&startPTS);
    }
    if (startDTS) {
        av_freep(&startDTS);
    }

    return 0;
}